package jscinttools.scint;
/* jhplot libraries */
import jhplot.P0D;
import jhplot.P1D;
import jhplot.PND;
import jhplot.H1D;
import jhplot.HPlotJa;
/* aida libraries */
import hep.aida.ITupleFactory;
import hep.aida.ITuple;
import hep.aida.IDataPointSetFactory;
import hep.aida.IDataPointSet;
import hep.aida.IDataPoint;
import hep.aida.IMeasurement;
import hep.aida.IHistogramFactory;
import hep.aida.IHistogram1D;
import hep.aida.ITree;
import hep.aida.ref.histogram.Measurement;
/* java libraries */
import java.lang.String;
import java.lang.StringBuilder;
import java.lang.System;
import java.lang.Math;
import java.lang.Double;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.File;
import java.awt.Color;
import java.awt.Font;
/* jscinttools libraries */
import jscinttools.utilities.plotterutilities.FileExtensions;
import jscinttools.utilities.aidautilities.AidaFactories;
import jscinttools.utilities.javautilities.MathUtil;

public class SpectraFactory{

	private SpectraFactory(){}
	
	public static SpectroFactory createSpectroFactory(){
		return new SpectroFactoryImp();
	}
}

final class SpectroFactoryImp implements SpectroFactory{
	private final static double ENERGY_WAVELENGTH = 1239.8;
	private final static String WL_TITLE = "Wavelength_nm";
	private final static String EN_TITLE = "Energy_eV";
	private final static String INTENS_TITLE = "Intensity_au";
	private final static String ERR_TITLE = "IntensityError_au";
	
	public Spectro createSpectroLPCML(String pathAndFile){
		return new SpectroLPCMLImp(pathAndFile);
	}
	
	public Spectro createSpectroLPCML(Spectro[] partSpectros){
		return new SpectroLPCMLImp(partSpectros);
	}
	
	private class SpectroLPCMLImp implements Spectro{

		private P0D wavelength_nm;
		private P0D energy_eV;
		private P0D intensity_au;
		private P0D err_au;
		private P0D wlP0D;
		private P0D enP0D;
		private P0D intensP0D;
		private P0D errP0D;
		private boolean nano = true;
		private boolean eV = !nano;
		private HPlotJa plotter;
		private P1D plotSpectro;
		private boolean showPlot = false;
		
		/** Constructor */
		public SpectroLPCMLImp(String pathAndFile){
			initAllP0Ds();
			initAllTempP0Ds();
			readDataFile(pathAndFile);
			plotSpectro = toP1D("Spectrum");
			initPlotter();
		}
		
		/** Constructor */
		public SpectroLPCMLImp(Spectro[] partSpectros){
			initAllP0Ds();
			initAllTempP0Ds();
			mergeP0Ds(partSpectros);
			plotSpectro = toP1D("Spectrum");
			initPlotter();
		}
		
		private void initAllP0Ds(){
			initWavelengthP0D();
			initEnP0D();
			initIntensP0D();
			initErrP0D();
		}
		
		private void initWavelengthP0D(){
			this.wavelength_nm = new P0D(WL_TITLE);
		}
		
		private void initEnP0D(){
			this.energy_eV = new P0D(EN_TITLE);
		}
		
		private void initIntensP0D(){
			this.intensity_au = new P0D(INTENS_TITLE);
		}
		
		private void initErrP0D(){
			this.err_au = new P0D(ERR_TITLE);
		}
		
		private void initAllTempP0Ds(){
			initTempWavelengthP0D();
			initTempEnP0D();
			initTempIntensP0D();
			initTempErrP0D();
		}
		
		private void initTempWavelengthP0D(){
			this.wlP0D = new P0D(WL_TITLE);
		}
		
		private void initTempEnP0D(){
			this.enP0D = new P0D(EN_TITLE);
		}
		
		private void initTempIntensP0D(){
			this.intensP0D = new P0D(INTENS_TITLE);
		}
		
		private void initTempErrP0D(){
			this.errP0D = new P0D(ERR_TITLE);
		}
		
		private void mergeP0Ds(Spectro[] partSpectros){
			SpectroP0DsMerger merger = new SpectroP0DsMerger(partSpectros);
			merger.merge();
		}
		
		class SpectroP0DsMerger{
                               
			private int len;
			private Spectro[] partSpectros;
			private P0D[] p0ds;
			//private P0D xWlP0D;
			//private P0D xEnP0D;
			//private P0D yIntensP0D;
			private int[][] overlapIndex = new int[len][2];
			private double[][] avVals = new double[len][2];
			private double[] scalFac;
			private int[][] edges;
			
			public SpectroP0DsMerger(Spectro[] partSpectros){
				this.partSpectros = partSpectros;
				this.len = partSpectros.length;
				this.overlapIndex = new int[len][2];
				this.avVals = new double[len][2];
				this.scalFac = new double[len];
				this.edges = new int[len][2];
				initP0Ds();
			}
			
			private void initP0Ds(){
				this.p0ds = new P0D[len];
				for (int i = 0; i < len; i++){
					partSpectros[i].eV_to_nm();
					p0ds[i] = partSpectros[i].toP0D("wl", 0);
				}
			}
			
			public void merge(){
				for (int i = 0; i < len; i++){
					partSpectros[i].nm_to_eV();
					enP0D.fill(partSpectros[i].axisValues(0));
					partSpectros[i].eV_to_nm();
					intensP0D.fill(partSpectros[i].axisValues(1));
					if (i == 0)
						initValues(i);
					else if (i == len - 1)
						endValues(i);
					else
						coreValues(i);
					//System.out.println("overlapIndex_0 = " + overlapIndex[i][0]);
					//System.out.println("overlapIndex_1 = " + overlapIndex[i][1]);
					//System.out.println("avVals_0 = " + avVals[i][0]);
					//System.out.println("avVals_1 = " + avVals[i][1]);
					//System.out.println("scalFac = " + scalFac[i]);
					//System.out.println("edges_0 = " + edges[i][0]);
					//System.out.println("edges_1 = " + edges[i][1]);
					//System.out.println("edge===================== = " + p0ds[i].get(edges[i][1]));
					fillP0Ds(i);
					clearTempP0Ds();
				}
			}
			
			private void initValues(int i){
				overlapIndex[i][0] = 0;
				overlapIndex[i][1] = findNextMin(i);
				avVals[i][0] = 1;
				avVals[i][1] = calcAverage(i, new int[]{overlapIndex[i][1], p0ds[i].size() - 1});
				//System.out.println("AvvvvvvvvvvEdgeeeee0 = " + p0ds[i].get(overlapIndex[i][1]));
				//System.out.println("AvvvvvvvvvvEdgeeeee1 = " + p0ds[i].get(p0ds[i].size() - 1));
				scalFac[i] = 1.0;
				edges[i][0] = 0;
				//edges[i][1] = p0ds[i].size() - 1;
				edges[i][1] = (p0ds[i].size() - 1 + overlapIndex[i][1]) / 2;
				//System.out.println("edgezzzzzzzzzzzzzzzzzzzz = " + p0ds[i].get(edges[i][1]));
			}
			
			private void endValues(int i){
				overlapIndex[i][0] = findPreviousMax(i);
				overlapIndex[i][1] = 0;
				avVals[i][0] = calcAverage(i, new int[]{0, overlapIndex[i][0]});
				avVals[i][1] = 1;
				//System.out.println("AvvvvvvvvvvEdgeeeee0 = " + xWlP0D.get(0));
				//System.out.println("AvvvvvvvvvvEdgeeeee1 = " + xWlP0D.get(overlapIndex[i][0]));
				scalFac[i] = scalFac[i - 1] * avVals[i - 1][1] / avVals[i][0];
				//edges[i][0] = overlapIndex[i][0];
				edges[i][0] = overlapIndex[i][0] / 2;
				edges[i][1] = p0ds[i].size() - 1;
			}
			
			private void coreValues(int i){
				overlapIndex[i][0] = findPreviousMax(i);
				overlapIndex[i][1] = findNextMin(i);
				avVals[i][0] = calcAverage(i, new int[]{0, overlapIndex[i][0]});
				avVals[i][1] = calcAverage(i, new int[]{overlapIndex[i][1], p0ds[i].size() - 1});
				scalFac[i] = scalFac[i - 1] * avVals[i - 1][1] / avVals[i][0];
				//edges[i][0] = overlapIndex[i][0];
				//edges[i][1] = p0ds[i].size() - 1;
				edges[i][0] = overlapIndex[i][0] / 2;
				edges[i][1] = (p0ds[i].size() - 1 + overlapIndex[i][1]) / 2;
			}
			
			private int findNextMin(int p0dIndex){
				double minVal = p0ds[p0dIndex + 1].get(0);
				if (p0ds[p0dIndex].contains(minVal))
					return p0ds[p0dIndex].find(minVal);
				else
					return findOverlappingIndexMin(p0dIndex, minVal);
			}
			
			private int findOverlappingIndexMin(int p0dIndex, double val){
				int index = p0ds[p0dIndex].size() - 1;
				while (p0ds[p0dIndex].get(index) > val)
					index--;
				return index - 1;
			}
			
			private int findPreviousMax(int p0dIndex){
				int p0dSize = p0ds[p0dIndex - 1].size();
				double maxVal = p0ds[p0dIndex - 1].get(p0dSize - 1);
				if (p0ds[p0dIndex].contains(maxVal))
					return p0ds[p0dIndex].find(maxVal);
				else
					return findOverlappingIndexMax(p0dIndex, maxVal);
			}
			
			private int findOverlappingIndexMax(int p0dIndex, double val){
				int index = 0;
				while (p0ds[p0dIndex].get(index) < val)
					index++;
				return index + 1;
			}
			
			private double calcAverage(int p0dIndex, int[] range){
				//P0D yIntensP0D = partSpectros[p0dIndex].toP0D("intensity", 1);
				double sum = 0.0;
				double numPoints = (range[1] - range[0] + 1) * 1.0;
				for (int i = range[0]; i <= range[1]; i++){
					//sum += p0ds[p0dIndex].get(i);
					sum += intensP0D.get(i);
					//System.out.println("======> avValsssss = " +yIntensP0D.get(i));
 				}
 				return sum / numPoints;
			}
			
			private void fillP0Ds(int p0dIndex){
				for (int i = edges[p0dIndex][0]; i <= edges[p0dIndex][1]; i++){
					wavelength_nm.add(p0ds[p0dIndex].get(i));
					energy_eV.add(enP0D.get(i));
					double unscalVal = intensP0D.get(i);
					double val = unscalVal * scalFac[p0dIndex];
					intensity_au.add(val);
					err_au.add(Math.sqrt(Math.abs(unscalVal)) * scalFac[p0dIndex]);
					//System.out.println("wavelength_nm = " + xWlP0D.get(i));
				}
			}
		}
		
		private void readDataFile(String pathAndFile){
			try{
				InputStream ips = new FileInputStream(pathAndFile);
				InputStreamReader ipsr = new InputStreamReader(ips);
				BufferedReader br = new BufferedReader(ipsr);
				String line;
				clearTempP0Ds();
				while ((line = br.readLine()) != null){
					if (!line.startsWith("%"))
						fillP0Ds(line);
				}
				int size = wlP0D.size();
				if (wlP0D.get(0) > wlP0D.get(size - 1))
					fillInvertedP0Ds();
				else
					fillOrderedP0Ds();
				br.close(); 
				clearTempP0Ds();
			}		
			catch (Exception e){
				System.out.println(e.toString());
			}
		}
		
		private void fillP0Ds(String line){
			String[] val = line.split(" ");
			double valX = Double.parseDouble(val[0]);
			double valY = Double.parseDouble(val[1]);
			wlP0D.add(valX);
			enP0D.add(ENERGY_WAVELENGTH / valX);
			intensP0D.add(valY);
			errP0D.add(Math.sqrt(Math.abs(valY)));
		}
		
		private void fillAllP0Ds(int i){
			wavelength_nm.add(wlP0D.get(i));
			energy_eV.add(enP0D.get(i));
			intensity_au.add(intensP0D.get(i));
			err_au.add(errP0D.get(i));
		}
		
		private void fillInvertedP0Ds(){
			for (int i = intensP0D.size() - 1; i >= 0; i--)
				fillAllP0Ds(i);
		}
		
		private void fillOrderedP0Ds(){
			for (int i = 0; i < intensP0D.size(); i++)
				fillAllP0Ds(i);
		}
		
		private void initPlotter(){
			this.plotter = new HPlotJa("Spectrum", 600, 600, 1, 1, true);
			this.plotter.setPad(0.125, 0.125, 0.75, 0.75);
			this.plotter.visible(showPlot);
			this.plotter.setAntiAlias(false);
			if (nano)
				this.plotter.setNameX("Wavelength (nm)");
			else
				this.plotter.setNameX("Energy (eV)");
			this.plotter.setNameY("Intensity (a.u.)");
		}
		
		public void nm_to_eV(){
			this.nano = false;
		
		}
		public void eV_to_nm(){
			this.nano = true;
		}
		
		public void add(Spectro spec){
			doOper(spec, "+");
		}
		
		public void substract(Spectro spec){
			doOper(spec, "-");
		}
		
		public void multiply(Spectro spec){
			doOper(spec, "*");
		}
		
		public void divide(Spectro spec){
			doOper(spec, "/");
		}
		
		private void doOper(Spectro spec, String symbol){
			OperationsSpectro os = new OperationsSpectro(spec, symbol);
			os.opSpec();
		}
		
		private class OperationsSpectro{
		
			private P0D ener;
			private P0D wl;
			private P0D intens;
			private P0D error;
			private String symbol;
			private double tempWL;
			private double tempIntens;
			private double tempErr;
			private double wlMin;
			private double wlMax;
			private double wlTempMin;
			private double wlTempMax;
			
			public OperationsSpectro(Spectro spec, String symbol){
				spec.nm_to_eV();
				this.ener = spec.toP0D(EN_TITLE, 0);
				spec.eV_to_nm();
				this.wl = spec.toP0D(WL_TITLE, 0);
				this.wlMin = wavelength_nm.get(0);
				this.wlMax = wavelength_nm.get(wavelength_nm.size() - 2);
				this.wlTempMin = wl.get(0);
				this.wlTempMax = wl.get(wl.size() - 2);
				this.intens = spec.toP0D(INTENS_TITLE, 1);
				this.error = spec.toP0D(ERR_TITLE, 2);
				this.symbol = symbol;
				this.tempWL = 0.0;
				this.tempIntens = 0.0;
				this.tempErr = 0.0;
			}
			
			public void opSpec(){
				//if (wavelength_nm.correlation(wl) == 1.0)
				//	intensity_au = intensity_au.oper(intens, symbol);
				//else if (wavelength_nm.correlation(wl) == 0.0)
				//	System.out.println("No corralation, cannot " + symbol + " these two Spectro");
				//else
				operate();
			}
			
			private void operate(){
				for (int i=0; i < wavelength_nm.size(); i++){
					this.tempWL = wavelength_nm.get(i);
					this.tempIntens = intensity_au.get(i);
					this.tempErr = err_au.get(i);
					if (wl.contains(tempWL))
						fillExactP0D();
					else
						fillExtrapolP0D();
				}
				updateIntensity();
				updateErr();
				clearTempP0Ds();
			}
			
			private void fillExactP0D(){
				int index2 = wl.find(tempWL);
				double[] val2 = new double[]{intens.get(index2), error.get(index2)};
				fillNewP0D(val2);
			}
			
			private void fillExtrapolP0D(){
				int i = 0;
				int size = wl.size();
				//System.out.println("wl : " + tempWL);
				while(wl.get(i) < tempWL && i < size){
					i++;
					//System.out.println(wl.get(i));
				}
				//System.out.println(wl.get(i));
				//if (wl.get(i) >= wlMin && wl.get(i) <= wlTempMax && tempWL <= wlMax)
				//if (i < size - 1 && wl.get(i) <= wlMax)
				if (i < size - 1)
					extrapolVal(i);
				//else{
				//	extrapolVal(wl.size() - 2);
				//	System.out.println("gggggggggggggggggggggggggg");
				//}
			}
			
			private void extrapolVal(int index){
				double wl1 = wl.get(index);
				double wl2 = wl.get(index + 1);
				double intens1 = intens.get(index);
				double intens2 = intens.get(index + 1);
				double err1 = error.get(index);
				double err2 = error.get(index + 1);
				double p1 = (intens2 - intens1) / (wl2 - wl1);
				double p0 = intens1 - (p1 * wl1);
				double p1Err = (err2 - err1) / (wl2 - wl1);
				//double p0Err = (intens1 + err1) - (p1Err * wl1);
				double p0Err = err1 - (p1Err * wl1);
				//fillNewP0D(new double[]{(intens1 + intens2) / 2.0, (err1 + err2) / 2.0});
				double exVal = (p1 * tempWL) + p0;
				//double exValErr = ((p1Err * tempWL) + p0Err) - exVal;
				double exValErr = (p1Err * tempWL) + p0Err;
				fillNewP0D(new double[]{exVal, exValErr});
			}
			
			private void fillNewP0D(double[] val2){
				double[] newVal = new double[2];
				if (symbol.equals("+"))
					newVal = new double[]{tempIntens + val2[0], tempErr  + val2[1]};
				else if (symbol.equals("-"))
					newVal = new double[]{tempIntens - val2[0], tempErr  + val2[1]};
				else if (symbol.equals("*"))
					newVal = new double[]{tempIntens * val2[0], MathUtil.uncorrProdErr(tempIntens, tempErr, val2[0], val2[1])};
				else if (symbol.equals("/") && val2[0] != 0)
					newVal = new double[]{tempIntens / val2[0], MathUtil.uncorrRatioErr(tempIntens, tempErr, val2[0], val2[1])};
				else if (symbol.equals("/") && val2[0] == 0)
					newVal = new double[]{Double.NaN, Double.NaN};
				//f (newVal[1] < 0){
				System.out.println("valOr = " + tempIntens);
				System.out.println("valOrErr = " + tempErr);
				System.out.println("op = " + val2[0]);
				System.out.println("opErr = " + val2[1]);
				System.out.println("valOp = " + newVal[0]);
				System.out.println("valOpErr = " + newVal[1]);
				//}
				intensP0D.add(newVal[0]);
				errP0D.add(newVal[1]);
			}
		}
		
		public void shift(double shiftVal){
			//intensP0D.clear();
			//for (int i = 0; i < intensity_au.size(); i++)
			//	intensP0D.add(intensity_au.get(i) + shiftVal);
			//updateIntensity();
			this.intensity_au = intensity_au.operShift(shiftVal);
			this.err_au = err_au.operShift(shiftVal);
		}
		
		public void scale(double scalFac){
			this.intensity_au = intensity_au.operScale(scalFac);
			this.err_au = err_au.operScale(scalFac);
		}
		
		public void normalizeArea(){
			int indexMin = 0;
			int indexMax = intensity_au.size();
			double integral = integrate();
			scale(1.0 / integral);
		}
		
		public void normalize(){
			double normFac = 1.0 / intensity_au.getMax();
			scale(normFac);
		}
		
		public void normalize(double min, double max){
			if (nano)
				norm_nm(min, max);
			else
				norm_eV(min, max);
		}
	
		private void norm_nm(double min, double max){
			int minIndex = wavelength_nm.find(min);
			int maxIndex = wavelength_nm.find(max);
			normRange(minIndex, maxIndex);
		}
	
		private void norm_eV(double min, double max){
			int minIndex = energy_eV.find(max);
			int maxIndex = energy_eV.find(min);
			normRange(minIndex, maxIndex);
		}
		
		private void normRange(int minIndex, int maxIndex){
			P0D rangeIntensity_au = intensity_au.range(minIndex, maxIndex);
			double normFac = 1.0 / rangeIntensity_au.getMax();
			scale(normFac);
		}
		
		public void toFile(String pathAndName){
			P1D spectro = toP1D("");
			spectro.toFile(pathAndName);
		}
		
		public P1D toP1D(String name){
			//if (nano)
			//	return new P1D(name, wavelength_nm, intensity_au);
			//else
			//	return new P1D(name, energy_eV, intensity_au);
			return new P1D(toDPS(name));
		}
		
		public PND toPND(String name){
			PND pnd = new PND(name);
			for (int i = 0; i < intensity_au.size(); i++){
				double wl = wavelength_nm.get(i);
				double en = energy_eV.get(i);
				double intens = intensity_au.get(i);
				double err = err_au.get(i);
				pnd.add(new double[]{wl, en, intens, err});
			}
			return pnd;
		}
		
		public H1D toH1D(String name){
			return new H1D(toHisto(name));
		}
		
		public P0D toP0D(String name, int axis){
			if (axis == 0)
				return xP0D(name);
			else if (axis == 1)
				return new P0D(name, false, intensity_au);
			else if (axis == 2)
				return new P0D(name, false, err_au);
			else
				return null;
		}
		
		private P0D xP0D(String name){
			if (nano)
				return new P0D(name, false, wavelength_nm);
			else
				return new P0D(name, false, energy_eV);
		}
					
		public double[] axisValues(int axis){
			if (axis == 0)
				return xValues();
			else if (axis == 1)
				return intensity_au.getArray();
			else if (axis == 2)
				return err_au.getArray();
			else
				return null;
		}
		
		private double[] xValues(){
			if (nano)
				return wavelength_nm.getArray();
			else
				return energy_eV.getArray();
		}
		
		public ITuple toTuple(String name){
			return toTuple(AidaFactories.TREE, name);
		}
		
		public ITuple toTuple(ITree tree, String pathAndName){
			ITupleFactory tupleFac = AidaFactories.ANALYSIS_FACTORY.createTupleFactory(tree);
			boolean sep = pathAndName.contains(File.separator);
			String[] sepPathName = new String[2];
			if (sep)
				sepPathName = splitPathName(pathAndName);
			else
				sepPathName = noSplitPathName(pathAndName);
			String columns = "double " + WL_TITLE + ", double" + EN_TITLE + ", double" + INTENS_TITLE + ", double" + ERR_TITLE;
			ITuple tuple = tupleFac.create(sepPathName[0], sepPathName[1], columns);
			fillTuple(tuple);
			return tuple;
		}
		
		private String[] splitPathName(String pathAndName){
			String[] sepPathName = new String[2];
			String[] allPart = pathAndName.split(File.separator);
			StringBuilder sb = new StringBuilder();
			int partLength = allPart.length;
			for (int i=0; i<partLength - 1; i++){
				sb.append(allPart[i]);
				if (i<partLength - 2)
					sb.append(File.separator);
			}
			sepPathName[0] = sb.toString();
			sepPathName[1] = allPart[partLength - 1];
			return sepPathName;
		}
		
		private String[] noSplitPathName(String pathAndName){
			return new String[]{"", pathAndName};
		}
		
		private void fillTuple(ITuple tuple){
			tuple.reset();
			tuple.start();
			for (int i=0; i<intensity_au.size(); i++){
				tuple.fill(0, wavelength_nm.get(i));
				tuple.fill(1, energy_eV.get(i));
				tuple.fill(2, intensity_au.get(i));
				tuple.fill(3, err_au.get(i));
				tuple.addRow();
			}
		}
		
		public IDataPointSet toDPS(String name){
			return toDPS(AidaFactories.TREE, name);
		}
		
		public IDataPointSet toDPS(ITree tree, String pathAndName){
			IDataPointSetFactory dpsFac = AidaFactories.ANALYSIS_FACTORY.createDataPointSetFactory(tree);
			IDataPointSet dps = dpsFac.create(pathAndName, 2);
			if (nano)
				fillDPS(dps, wavelength_nm);
			else
				fillDPS(dps, energy_eV);
			return dps;
		}
		
		private void fillDPS(IDataPointSet dps, P0D xP0D){
			for (int i=0; i<xP0D.size(); i++){
				IDataPoint dp = dps.addPoint();
				IMeasurement mX = dp.coordinate(0);
				double x = xP0D.get(i);
				mX.setValue(x);
				IMeasurement mY = dp.coordinate(1);
				double y = intensity_au.get(i);
				double yErr = err_au.get(i);
				mY.setValue(y);
				mY.setErrorPlus(yErr);
				mY.setErrorMinus(yErr);
			}
		}
		
		public IHistogram1D toHisto(String name){
			return toHisto(AidaFactories.TREE, name);
		}
		
		public IHistogram1D toHisto(ITree tree, String pathAndName){
			if (nano)
				return fillHisto(tree, pathAndName, wavelength_nm);
			else
				return fillHisto(tree, pathAndName, energy_eV);
		}
		
		private IHistogram1D fillHisto(ITree tree, String pathAndName, P0D xP0D){
			IHistogramFactory histoFac = AidaFactories.ANALYSIS_FACTORY.createHistogramFactory(tree);
			int bin = intensity_au.size();
			double min = xP0D.getMin();
			double max = xP0D.getMax();
			IHistogram1D histo = histoFac.createHistogram1D(pathAndName, bin, min, max);
			for (int i=0; i<bin; i++){
				double x = xP0D.get(i);
				double y = intensity_au.get(i);
				histo.fill(x, y);
			}
			return histo;
		}
		
		public void showPlot(){
			doPlot();
			showPlot = true;
			plotter.visible(showPlot);
		}
		
		public void savePlot(String pathAndName){
			doPlot();
			for (int i = 0; i < FileExtensions.FIG_TYPES.length; i++){
				plotter.export(pathAndName + FileExtensions.FIG_TYPES[i]);
				System.out.println("Writing File : " + pathAndName + FileExtensions.FIG_TYPES[i]);
			}
		}
		
		private void doPlot(){
			plotSpectro.clear();
			plotSpectro = toP1D("Spectrum");
			plotSpectro.setStyle("lp");
			plotSpectro.setSymbolSize(0);
			plotSpectro.setPenWidth(2);
			plotSpectro.setColor(Color.BLUE);
			plotSpectro.setErrAll(false);
			double xMin = plotSpectro.getMin(0);
			double xMax = plotSpectro.getMax(0);
			double yMin = plotSpectro.getMin(1);
			double yMax = plotSpectro.getMax(1);
			plotter.draw(plotSpectro);
			plotter.setRange(xMin, xMax, yMin, yMax);
			plotter.update();
			double[] val = plotSpectro.getArrayY();
			double[] valErr = plotSpectro.getArrayYupper();
			//for (int i = 0; i < val.length; i++){
			//	System.out.println("val = " + val[i]);
			//	System.out.println("valErr = " + valErr[i]);
			//}
		}
		
		public void roundAbscissa(){
			wlP0D.clear(); 
			enP0D.clear();
			for (int i=0; i<wavelength_nm.size(); i++){
				double val = wavelength_nm.get(i);
				double round = Math.rint(val);
				wlP0D.add(round); 
				enP0D.add(ENERGY_WAVELENGTH / round);
				//wavelength_nm.set(i, round);
				//double enVal = energy_eV.get(i);
				//energy_eV.set(i, ENERGY_WAVELENGTH / round);
			}
			updateWL();
			updateEn();
		}
			
		public void removeOutliers(int halfNumPnts, double numSigmas){
			//int halfNumPnts = 2;
			//double numSigmas = 3;
			class LPCMLOutliersImp implements Outliers{
				
				public boolean isThereOutliers(int halfNumPnts, double numSigmas){
					boolean outl = false;	
					int size = intensity_au.size();
					int i = 0;
					while (!outl && i < size){
						outl = isOutlier(i, halfNumPnts, numSigmas);
						i++;
					}
					return outl;
				}
				
				public void removeOutliers(int halfNumPnts, double numSigmas){
					boolean[] outliers = findOutliers(halfNumPnts, numSigmas);
					clearTempP0Ds();
					int size = outliers.length;
					for(int i = 0; i < size; i++){
						if (!outliers[i])
							fillTempP0Ds(i);
					}
					updateAll();
				}
				
				public boolean[] findOutliers(int halfNumPnts, double numSigmas){
					int size = intensity_au.size();
					boolean[] outliers = new boolean[size];
					for (int i = 0; i < size; i++){
						outliers[i] = isOutlier(i, halfNumPnts, numSigmas);
						//if (outliers[i])
						//	System.out.println(wavelength_nm.get(i));
					}
					return outliers;
				}
				
				private boolean isOutlier(int index, int halfNumPnts, double numSigmas) {
					double value = intensity_au.get(index);
					double[] array = surroundArray(index, halfNumPnts);
					double average  = calcAverage(array);
					double variance = calcVariance(array, average);
					double distSquared = Math.pow(average - value, 2);
					double refVariance = variance * Math.pow(numSigmas, 2);
					return (distSquared > refVariance);
				}
				
				private double calcAverage(double[] array){
					int size = array.length;
					double average = 0.0;
					for (int i = 0; i < size; i++){
						average += array[i];
					}
					return average / (double) size;
				}
				
				private double calcVariance(double[] array, double average){
					int size = array.length;
					double variance = 0.0;
					for (int i = 0; i < size; i++){
						variance += Math.pow(array[i] - average, 2);
					}
					return variance / (double) size;
				}
				
				private double[] surroundArray(int index, int halfNumPnts){
					if (index < halfNumPnts)
						return calcLowArray(index, halfNumPnts);
					else if (index >= intensity_au.size() - halfNumPnts)
						return calcHighArray(index, halfNumPnts);
					else
						return calcCoreArray(index, halfNumPnts);
				}
				
				private double[] calcLowArray(int index, int halfNumPnts){
					return calcArray(0, index + halfNumPnts, index);
				}
				
				private double[] calcHighArray(int index, int halfNumPnts){
					return calcArray(index - halfNumPnts, intensity_au.size() - 1, index);
				}
				
				private double[] calcCoreArray(int index, int halfNumPnts){
					return calcArray(index - halfNumPnts, index + halfNumPnts, index);
				}
				
				private double[] calcArray(int start, int stop, int index){
					//double[] array = new double[stop - start];
					P0D array = new P0D();
					for (int i = start; i <= stop; i++){
						if (i != index)
							array.add(intensity_au.get(i));
					}
					return array.getArray();
				}
			}
			
			LPCMLOutliersImp outliers = new LPCMLOutliersImp();
			int cnt = 0;
			while (outliers.isThereOutliers(halfNumPnts, numSigmas)){
				outliers.removeOutliers(halfNumPnts, numSigmas);
				cnt++;
			}
			System.out.println("No more outliers found after " + cnt + " iteration(s)");
		}
		
		public void averageMultipleValues(){
			clearTempP0Ds();
			//for (int i=0; i<wavelength_nm.size(); i++){
			int i = 0;
			int size = wavelength_nm.size();
			while (i < size){
				double xVal = wavelength_nm.get(i);
				double yVal = intensity_au.get(i);
				double yErr = err_au.get(i);
				//System.out.println("wl_i = " + xVal);
				int j = i;
				int cnt = 1;
				while(wavelength_nm.get(j) == xVal && j < size - 1){
					//System.out.println("wl_j = " + xVal);
					yVal += intensity_au.get(j);
					yErr += err_au.get(j);
					j++;
					cnt++;
				}
				yVal /= (double) cnt;
				yErr /= (double) cnt;
				wlP0D.add(xVal);
				enP0D.add(ENERGY_WAVELENGTH / xVal);
				intensP0D.add(yVal);
				errP0D.add(yErr);
				i = j;
				if (i == size -1)
					break;
			}
			updateAll();
		}
		
		public void smooth(int average){
			int size = intensity_au.size();
			//intensP0D.clear();
			initTempIntensP0D();
			initTempErrP0D();
			for (int i = 0; i < size; i++){
				//double[] yVal = {0.0, 0.0};
				int[] edges = new int[2];
				if ((i - average) < 0)
					edges =  new int[]{0,  i + average};
				else if ((i - average) >= 0 && (i + average) < size)
					edges = new int[]{i - average,  i + average};
				else
					edges =  new int[]{i - average,  size - 1};
				//System.out.println("======================>");
				double numVal = (average * 2.0) + 1.0;
				double[] yVal = summedIntensities(edges);
				intensP0D.add(yVal[0] / numVal);
				errP0D.add(yVal[1] / numVal);
				//System.out.println(yVal[0] / numVal);
				//System.out.println(yVal[1] / numVal);
			}
			updateIntensity();
			updateErr();
		}
		
		private double[] summedIntensities(int[] edge){
			double[] val = {0.0, 0.0};
			for (int i = edge[0]; i <= edge[1]; i++){
				val[0] += intensity_au.get(i);
				val[1] += err_au.get(i);
			}
			return val;
		}
		
		public void cutEdges(double min, double max){
			if (nano)
				cutEdgesWL(min, max);
			else
				cutEdgesEn(min, max);
		}
		
		private void cutEdgesWL(double min, double max){
			int indexMin = 0;
			int indexMax = 0;
			if (wavelength_nm.contains(min))
				indexMin = wavelength_nm.find(min);
			else
				indexMin = findMinIndexWL(min);
			if (wavelength_nm.contains(max))
				indexMax = wavelength_nm.find(max);
			else
				indexMax = findMaxIndexWL(max);
			doEdgesCut(indexMin, indexMax);
		}
		
		private void cutEdgesEn(double min, double max){
			int indexMin = 0;
			int indexMax = 0;
			if (energy_eV.contains(min))
				indexMin = energy_eV.find(min);
			else
				indexMin = findMinIndexWL(min);
			if (energy_eV.contains(max))
				indexMax = energy_eV.find(max);
			else
				indexMax = findMaxIndexWL(max);
			doEdgesCut(indexMin, indexMax);
		}
		
		private void doEdgesCut(int indexMin, int indexMax){
			clearTempP0Ds();
			for (int i = indexMin; i < indexMax + 1; i++){
				double val = 0.0;
				val = wavelength_nm.get(i);
				wlP0D.add(val);
				val = energy_eV.get(i);
				enP0D.add(val);
				val = intensity_au.get(i);
				intensP0D.add(val);
				val = err_au.get(i);
				errP0D.add(val);
			}
			updateAll();
		}
		
		public double integrate(){
			IMeasurement meas = integrateAndErr();
			return meas.value();
		}
		
		public double integrate(double min, double max){
			IMeasurement meas = integrateAndErr(min, max);
			return meas.value();
		}
		
		public IMeasurement integrateAndErr(){
			int indexMin = 0;
			int indexMax = intensity_au.size();
			if (nano)
				return calcIntegWL(indexMin, indexMax);
			else
				return calcIntegEn(indexMin, indexMax);
		}
		
		public IMeasurement integrateAndErr(double min, double max){
			if (nano)
				return integrateWL(min, max);
			else
				return integrateEn(min, max);
		}
		
		private IMeasurement integrateWL(double min, double max){
			int indexMin = 0;
			int indexMax = 0;
			if (wavelength_nm.contains(min))
				indexMin = wavelength_nm.find(min);
			else
				indexMin = findMinIndexWL(min);
			if (wavelength_nm.contains(max))
				indexMax = wavelength_nm.find(max);
			else
				indexMax = findMaxIndexWL(max);
			return calcIntegWL(indexMin, indexMax);
		}
		
		private IMeasurement calcIntegWL(int indexMin, int indexMax){
			double integral = 0.0;
			double err = 0.0;
			for (int i = indexMin; i < indexMax - 1; i++){
				double dWL = wavelength_nm.get(i + 1) - wavelength_nm.get(i);
				integral += (dWL * intensity_au.get(i));
				err += (dWL * err_au.get(i));
			}
			return new Measurement(integral, err);
		}
			
		private IMeasurement integrateEn(double min, double max){
			int indexMin = 0;
			int indexMax = 0;
			if (energy_eV.contains(min))
				indexMin = energy_eV.find(min);
			else
				indexMin = findMinIndexEn(min);
			if (energy_eV.contains(max))
				indexMax = energy_eV.find(max);
			else
				indexMax = findMinIndexEn(max);
			return calcIntegEn(indexMin, indexMax);
		}
		
		private IMeasurement calcIntegEn(int indexMin, int indexMax){
			double integral = 0.0;
			double err = 0.0;
			for (int i = indexMin; i < indexMax - 1; i++){
				double dWL = energy_eV.get(i + 1) - energy_eV.get(i);
				integral += (dWL * intensity_au.get(i));
				err += (dWL * err_au.get(i));
			}
			return new Measurement(integral, err);
		}
		
		private void updateWL(){
			//wavelength_nm.clear();
			initWavelengthP0D();
			for (int i=0; i<wlP0D.size(); i++){
				double val = wlP0D.get(i);
				wavelength_nm.add(val);
			}
			//wlP0D.clear();
			initTempWavelengthP0D();
		}
			
		private void updateEn(){
			//energy_eV.clear();
			initEnP0D();
			for (int i = 0; i < enP0D.size(); i++){
				double val = enP0D.get(i);
				energy_eV.add(val);
			}
			//enP0D.clear();
			initTempEnP0D();
		}
			
		private void updateIntensity(){
			//intensity_au.clear();
			initIntensP0D();
			for (int i=0; i<intensP0D.size(); i++){
				double val = intensP0D.get(i);
				intensity_au.add(val);
			}
			//intensP0D.clear();
			initTempIntensP0D();
		}
		
		private void updateErr(){
			//err_au.clear();
			initErrP0D();
			for (int i=0; i<errP0D.size(); i++){
				double val = errP0D.get(i);
				err_au.add(val);
			}
			//errP0D.clear();
			initTempErrP0D();
		}
		
		private void updateAll(){
			//wavelength_nm.clear();
			//energy_eV.clear();
			//intensity_au.clear();
			initAllP0Ds();
			for (int i=0; i<wlP0D.size(); i++){
				double val = 0.0;
				val = wlP0D.get(i);
				wavelength_nm.add(val);
				val = enP0D.get(i);
				energy_eV.add(val);
				val = intensP0D.get(i);
				intensity_au.add(val);
				val = errP0D.get(i);
				err_au.add(val);
			}
			clearTempP0Ds();
		}
		
		private int findMinIndexWL(double min){
			int index = 0;
			while (wavelength_nm.get(index) < min)
				index++;
			return index + 1;
		}
		
		private int findMaxIndexWL(double max){
			int index = wavelength_nm.size() - 1;
			while (wavelength_nm.get(index) > max)
				index--;
			return index - 1;
		}
		private int findMinIndexEn(double min){
			int index = energy_eV.size() - 1;
			while (energy_eV.get(index) > min)
				index--;
			return index - 1;
		}
		
		private int findMaxIndexEn(double max){
			int index = 0;
			while (energy_eV.get(index) < max)
				index++;
			return index + 1;
		}
							
		private void fillTempP0Ds(int index){
			wlP0D.add(wavelength_nm.get(index));
			enP0D.add(energy_eV.get(index));
			intensP0D.add(intensity_au.get(index));
			errP0D.add(err_au.get(index));
		}
		
		private void clearTempP0Ds(){
			//wlP0D.clear();
			//enP0D.clear();
			//intensP0D.clear();
			initAllTempP0Ds();
		}
	}
	
	public Spectro createSpectroQE(String pathAndFile){
		return new SpectroQEImp(pathAndFile);
	}
	
	private class SpectroQEImp implements Spectro{

		private P0D wavelength_nm;
		private P0D energy_eV;
		private P0D intensity_au;
		private P0D err_au;
		private P0D wlP0D;
		private P0D enP0D;
		private P0D intensP0D;
		private P0D errP0D;
		private boolean nano = true;
		private boolean eV = !nano;
		private HPlotJa plotter;
		private P1D plotSpectro;
		private boolean showPlot = false;
		
		/** Constructor */
		public SpectroQEImp(String pathAndFile){
			initAllP0Ds();
			initAllTempP0Ds();
			readDataFile(pathAndFile);
			plotSpectro = toP1D("Spectrum");
			initPlotter();
		}
		
		///** Constructor */
		//public SpectroLPCMLImp(Spectro[] partSpectros){
		//	initAllP0Ds();
		//	initAllTempP0Ds();
		//	mergeP0Ds(partSpectros);
		//	plotSpectro = toP1D("Spectrum");
		//	initPlotter();
		//}
		
		private void initAllP0Ds(){
			initWavelengthP0D();
			initEnP0D();
			initIntensP0D();
			initErrP0D();
		}
		
		private void initWavelengthP0D(){
			this.wavelength_nm = new P0D(WL_TITLE);
		}
		
		private void initEnP0D(){
			this.energy_eV = new P0D(EN_TITLE);
		}
		
		private void initIntensP0D(){
			this.intensity_au = new P0D(INTENS_TITLE);
		}
		
		private void initErrP0D(){
			this.err_au = new P0D(ERR_TITLE);
		}
		
		private void initAllTempP0Ds(){
			initTempWavelengthP0D();
			initTempEnP0D();
			initTempIntensP0D();
			initTempErrP0D();
		}
		
		private void initTempWavelengthP0D(){
			this.wlP0D = new P0D(WL_TITLE);
		}
		
		private void initTempEnP0D(){
			this.enP0D = new P0D(EN_TITLE);
		}
		
		private void initTempIntensP0D(){
			this.intensP0D = new P0D(INTENS_TITLE);
		}
		
		private void initTempErrP0D(){
			this.errP0D = new P0D(ERR_TITLE);
		}
		
		//private void mergeP0Ds(Spectro[] partSpectros){
		//	SpectroP0DsMerger merger = new SpectroP0DsMerger(partSpectros);
		//	merger.merge();
		//}
		//
		//class SpectroP0DsMerger{
                //               
		//	private int len;
		//	private Spectro[] partSpectros;
		//	private P0D[] p0ds;
		//	//private P0D xWlP0D;
		//	//private P0D xEnP0D;
		//	//private P0D yIntensP0D;
		//	private int[][] overlapIndex = new int[len][2];
		//	private double[][] avVals = new double[len][2];
		//	private double[] scalFac;
		//	private int[][] edges;
		//	
		//	public SpectroP0DsMerger(Spectro[] partSpectros){
		//		this.partSpectros = partSpectros;
		//		this.len = partSpectros.length;
		//		this.overlapIndex = new int[len][2];
		//		this.avVals = new double[len][2];
		//		this.scalFac = new double[len];
		//		this.edges = new int[len][2];
		//		initP0Ds();
		//	}
		//	
		//	private void initP0Ds(){
		//		this.p0ds = new P0D[len];
		//		for (int i = 0; i < len; i++){
		//			partSpectros[i].eV_to_nm();
		//			p0ds[i] = partSpectros[i].toP0D("wl", 0);
		//		}
		//	}
		//	
		//	public void merge(){
		//		for (int i = 0; i < len; i++){
		//			partSpectros[i].nm_to_eV();
		//			enP0D.fill(partSpectros[i].axisValues(0));
		//			partSpectros[i].eV_to_nm();
		//			intensP0D.fill(partSpectros[i].axisValues(1));
		//			if (i == 0)
		//				initValues(i);
		//			else if (i == len - 1)
		//				endValues(i);
		//			else
		//				coreValues(i);
		//			//System.out.println("overlapIndex_0 = " + overlapIndex[i][0]);
		//			//System.out.println("overlapIndex_1 = " + overlapIndex[i][1]);
		//			//System.out.println("avVals_0 = " + avVals[i][0]);
		//			//System.out.println("avVals_1 = " + avVals[i][1]);
		//			//System.out.println("scalFac = " + scalFac[i]);
		//			//System.out.println("edges_0 = " + edges[i][0]);
		//			//System.out.println("edges_1 = " + edges[i][1]);
		//			//System.out.println("edge===================== = " + p0ds[i].get(edges[i][1]));
		//			fillP0Ds(i);
		//			clearTempP0Ds();
		//		}
		//	}
		//	
		//	private void initValues(int i){
		//		overlapIndex[i][0] = 0;
		//		overlapIndex[i][1] = findNextMin(i);
		//		avVals[i][0] = 1;
		//		avVals[i][1] = calcAverage(i, new int[]{overlapIndex[i][1], p0ds[i].size() - 1});
		//		//System.out.println("AvvvvvvvvvvEdgeeeee0 = " + p0ds[i].get(overlapIndex[i][1]));
		//		//System.out.println("AvvvvvvvvvvEdgeeeee1 = " + p0ds[i].get(p0ds[i].size() - 1));
		//		scalFac[i] = 1.0;
		//		edges[i][0] = 0;
		//		//edges[i][1] = p0ds[i].size() - 1;
		//		edges[i][1] = (p0ds[i].size() - 1 + overlapIndex[i][1]) / 2;
		//		//System.out.println("edgezzzzzzzzzzzzzzzzzzzz = " + p0ds[i].get(edges[i][1]));
		//	}
		//	
		//	private void endValues(int i){
		//		overlapIndex[i][0] = findPreviousMax(i);
		//		overlapIndex[i][1] = 0;
		//		avVals[i][0] = calcAverage(i, new int[]{0, overlapIndex[i][0]});
		//		avVals[i][1] = 1;
		//		//System.out.println("AvvvvvvvvvvEdgeeeee0 = " + xWlP0D.get(0));
		//		//System.out.println("AvvvvvvvvvvEdgeeeee1 = " + xWlP0D.get(overlapIndex[i][0]));
		//		scalFac[i] = scalFac[i - 1] * avVals[i - 1][1] / avVals[i][0];
		//		//edges[i][0] = overlapIndex[i][0];
		//		edges[i][0] = overlapIndex[i][0] / 2;
		//		edges[i][1] = p0ds[i].size() - 1;
		//	}
		//	
		//	private void coreValues(int i){
		//		overlapIndex[i][0] = findPreviousMax(i);
		//		overlapIndex[i][1] = findNextMin(i);
		//		avVals[i][0] = calcAverage(i, new int[]{0, overlapIndex[i][0]});
		//		avVals[i][1] = calcAverage(i, new int[]{overlapIndex[i][1], p0ds[i].size() - 1});
		//		scalFac[i] = scalFac[i - 1] * avVals[i - 1][1] / avVals[i][0];
		//		//edges[i][0] = overlapIndex[i][0];
		//		//edges[i][1] = p0ds[i].size() - 1;
		//		edges[i][0] = overlapIndex[i][0] / 2;
		//		edges[i][1] = (p0ds[i].size() - 1 + overlapIndex[i][1]) / 2;
		//	}
		//	
		//	private int findNextMin(int p0dIndex){
		//		double minVal = p0ds[p0dIndex + 1].get(0);
		//		if (p0ds[p0dIndex].contains(minVal))
		//			return p0ds[p0dIndex].find(minVal);
		//		else
		//			return findOverlappingIndexMin(p0dIndex, minVal);
		//	}
		//	
		//	private int findOverlappingIndexMin(int p0dIndex, double val){
		//		int index = p0ds[p0dIndex].size() - 1;
		//		while (p0ds[p0dIndex].get(index) > val)
		//			index--;
		//		return index - 1;
		//	}
		//	
		//	private int findPreviousMax(int p0dIndex){
		//		int p0dSize = p0ds[p0dIndex - 1].size();
		//		double maxVal = p0ds[p0dIndex - 1].get(p0dSize - 1);
		//		if (p0ds[p0dIndex].contains(maxVal))
		//			return p0ds[p0dIndex].find(maxVal);
		//		else
		//			return findOverlappingIndexMax(p0dIndex, maxVal);
		//	}
		//	
		//	private int findOverlappingIndexMax(int p0dIndex, double val){
		//		int index = 0;
		//		while (p0ds[p0dIndex].get(index) < val)
		//			index++;
		//		return index + 1;
		//	}
		//	
		//	private double calcAverage(int p0dIndex, int[] range){
		//		//P0D yIntensP0D = partSpectros[p0dIndex].toP0D("intensity", 1);
		//		double sum = 0.0;
		//		double numPoints = (range[1] - range[0] + 1) * 1.0;
		//		for (int i = range[0]; i <= range[1]; i++){
		//			//sum += p0ds[p0dIndex].get(i);
		//			sum += intensP0D.get(i);
		//			//System.out.println("======> avValsssss = " +yIntensP0D.get(i));
 		//		}
 		//		return sum / numPoints;
		//	}
		//	
		//	private void fillP0Ds(int p0dIndex){
		//		for (int i = edges[p0dIndex][0]; i <= edges[p0dIndex][1]; i++){
		//			wavelength_nm.add(p0ds[p0dIndex].get(i));
		//			energy_eV.add(enP0D.get(i));
		//			double unscalVal = intensP0D.get(i);
		//			double val = unscalVal * scalFac[p0dIndex];
		//			intensity_au.add(val);
		//			err_au.add(Math.sqrt(Math.abs(unscalVal)) * scalFac[p0dIndex]);
		//			//System.out.println("wavelength_nm = " + xWlP0D.get(i));
		//		}
		//	}
		//}
		
		private void readDataFile(String pathAndFile){
			try{
				InputStream ips = new FileInputStream(pathAndFile);
				InputStreamReader ipsr = new InputStreamReader(ips);
				BufferedReader br = new BufferedReader(ipsr);
				String line;
				clearTempP0Ds();
				while ((line = br.readLine()) != null){
					if (!line.startsWith("%"))
						fillP0Ds(line);
				}
				int size = wlP0D.size();
				if (wlP0D.get(0) > wlP0D.get(size - 1))
					fillInvertedP0Ds();
				else
					fillOrderedP0Ds();
				br.close(); 
				clearTempP0Ds();
			}		
			catch (Exception e){
				System.out.println(e.toString());
			}
		}
		
		private void fillP0Ds(String line){
			String[] val = line.split("\t");
			double valX = Double.parseDouble(val[0]);
			double valY = Double.parseDouble(val[1]);
			wlP0D.add(valX);
			enP0D.add(ENERGY_WAVELENGTH / valX);
			intensP0D.add(valY);
			errP0D.add(0.0);
		}
		
		private void fillAllP0Ds(int i){
			wavelength_nm.add(wlP0D.get(i));
			energy_eV.add(enP0D.get(i));
			intensity_au.add(intensP0D.get(i));
			err_au.add(errP0D.get(i));
		}
		
		private void fillInvertedP0Ds(){
			for (int i = intensP0D.size() - 1; i >= 0; i--)
				fillAllP0Ds(i);
		}
		
		private void fillOrderedP0Ds(){
			for (int i = 0; i < intensP0D.size(); i++)
				fillAllP0Ds(i);
		}
		
		private void initPlotter(){
			this.plotter = new HPlotJa("Spectrum", 600, 600, 1, 1, true);
			this.plotter.setPad(0.125, 0.125, 0.75, 0.75);
			this.plotter.visible(showPlot);
			this.plotter.setAntiAlias(false);
			if (nano)
				this.plotter.setNameX("Wavelength (nm)");
			else
				this.plotter.setNameX("Energy (eV)");
			this.plotter.setNameY("Intensity (a.u.)");
		}
		
		public void nm_to_eV(){
			this.nano = false;
		
		}
		public void eV_to_nm(){
			this.nano = true;
		}
		
		public void add(Spectro spec){
			doOper(spec, "+");
		}
		
		public void substract(Spectro spec){
			doOper(spec, "-");
		}
		
		public void multiply(Spectro spec){
			doOper(spec, "*");
		}
		
		public void divide(Spectro spec){
			doOper(spec, "/");
		}
		
		private void doOper(Spectro spec, String symbol){
			OperationsSpectro os = new OperationsSpectro(spec, symbol);
			os.opSpec();
		}
		
		private class OperationsSpectro{
		
			private P0D ener;
			private P0D wl;
			private P0D intens;
			private P0D error;
			private String symbol;
			private double tempWL;
			private double tempIntens;
			private double tempErr;
			
			public OperationsSpectro(Spectro spec, String symbol){
				spec.nm_to_eV();
				this.ener = spec.toP0D(EN_TITLE, 0);
				spec.eV_to_nm();
				this.wl = spec.toP0D(WL_TITLE, 0);
				this.intens = spec.toP0D(INTENS_TITLE, 1);
				this.error = spec.toP0D(ERR_TITLE, 2);
				this.symbol = symbol;
				this.tempWL = 0.0;
				this.tempIntens = 0.0;
				this.tempErr = 0.0;
			}
			
			public void opSpec(){
				//if (wavelength_nm.correlation(wl) == 1.0)
				//	intensity_au = intensity_au.oper(intens, symbol);
				//else if (wavelength_nm.correlation(wl) == 0.0)
				//	System.out.println("No corralation, cannot " + symbol + " these two Spectro");
				//else
				operate();
			}
			
			private void operate(){
				for (int i=0; i < wavelength_nm.size(); i++){
					this.tempWL = wavelength_nm.get(i);
					this.tempIntens = intensity_au.get(i);
					this.tempErr = err_au.get(i);
					if (wl.contains(tempWL))
						fillExactP0D();
					else
						fillExtrapolP0D();
				}
				updateIntensity();
				updateErr();
				clearTempP0Ds();
			}
			
			private void fillExactP0D(){
				int index2 = wl.find(tempWL);
				double[] val2 = new double[]{intens.get(index2), error.get(index2)};
				fillNewP0D(val2);
			}
			
			private void fillExtrapolP0D(){
				int i = 0;
				int size = wl.size();
				while(wl.get(i) < tempWL && i < size)
					i++;
				if (i < size - 1)
					extrapolVal(i);
			}
			
			private void extrapolVal(int index){
				double wl1 = wl.get(index);
				double wl2 = wl.get(index + 1);
				double intens1 = intens.get(index);
				double intens2 = intens.get(index + 1);
				double err1 = error.get(index);
				double err2 = error.get(index + 1);
				double p1 = (intens2 - intens1) / (wl2 - wl1);
				double p0 = intens1 - (p1 * wl1);
				double p1Err = (err2 - err1) / (wl2 - wl1);
				double p0Err = (intens1 + err1) - (p1Err * wl1);
				//fillNewP0D(new double[]{(intens1 + intens2) / 2.0, (err1 + err2) / 2.0});
				double exVal = (p1 * tempWL) + p0;
				double exValErr = ((p1Err * tempWL) + p0Err) - exVal;
				fillNewP0D(new double[]{exVal, exValErr});
			}
			
			private void fillNewP0D(double[] val2){
				double[] newVal = new double[2];
				if (symbol.equals("+"))
					newVal = new double[]{tempIntens + val2[0], tempErr  + val2[1]};
				else if (symbol.equals("-"))
					newVal = new double[]{tempIntens - val2[0], tempErr  + val2[1]};
				else if (symbol.equals("*"))
					newVal = new double[]{tempIntens * val2[0], MathUtil.uncorrProdErr(tempIntens, tempErr, val2[0], val2[1])};
				else if (symbol.equals("/") && val2[0] != 0)
					newVal = new double[]{tempIntens / val2[0], MathUtil.uncorrRatioErr(tempIntens, tempErr, val2[0], val2[1])};
				else if (symbol.equals("/") && val2[0] == 0)
					newVal = new double[]{Double.NaN, Double.NaN};
				//if (newVal[1] < 0){
				//	System.out.println("valOr = " + tempIntens);
				//	System.out.println("valOrErr = " + tempErr);
				//	System.out.println("valOp = " + newVal[0]);
				//	System.out.println("valOpErr = " + newVal[1]);
				//}
				intensP0D.add(newVal[0]);
				errP0D.add(newVal[1]);
			}
		}
		
		public void shift(double shiftVal){
			//intensP0D.clear();
			//for (int i = 0; i < intensity_au.size(); i++)
			//	intensP0D.add(intensity_au.get(i) + shiftVal);
			//updateIntensity();
			this.intensity_au = intensity_au.operShift(shiftVal);
			this.err_au = err_au.operShift(shiftVal);
		}
		
		public void scale(double scalFac){
			this.intensity_au = intensity_au.operScale(scalFac);
			this.err_au = err_au.operScale(scalFac);
		}
		
		public void normalizeArea(){
			int indexMin = 0;
			int indexMax = intensity_au.size();
			double integral = integrate();
			scale(1.0 / integral);
		}
		
		public void normalize(){
			double normFac = 1.0 / intensity_au.getMax();
			scale(normFac);
		}
		
		public void normalize(double min, double max){
			if (nano)
				norm_nm(min, max);
			else
				norm_eV(min, max);
		}
	
		private void norm_nm(double min, double max){
			int minIndex = wavelength_nm.find(min);
			int maxIndex = wavelength_nm.find(max);
			normRange(minIndex, maxIndex);
		}
	
		private void norm_eV(double min, double max){
			int minIndex = energy_eV.find(max);
			int maxIndex = energy_eV.find(min);
			normRange(minIndex, maxIndex);
		}
		
		private void normRange(int minIndex, int maxIndex){
			P0D rangeIntensity_au = intensity_au.range(minIndex, maxIndex);
			double normFac = 1.0 / rangeIntensity_au.getMax();
			scale(normFac);
		}
		
		public void toFile(String pathAndName){
			P1D spectro = toP1D("");
			spectro.toFile(pathAndName);
		}
		
		public P1D toP1D(String name){
			//if (nano)
			//	return new P1D(name, wavelength_nm, intensity_au);
			//else
			//	return new P1D(name, energy_eV, intensity_au);
			return new P1D(toDPS(name));
		}
		
		public PND toPND(String name){
			PND pnd = new PND(name);
			for (int i = 0; i < intensity_au.size(); i++){
				double wl = wavelength_nm.get(i);
				double en = energy_eV.get(i);
				double intens = intensity_au.get(i);
				double err = err_au.get(i);
				pnd.add(new double[]{wl, en, intens, err});
			}
			return pnd;
		}
		
		public H1D toH1D(String name){
			return new H1D(toHisto(name));
		}
		
		public P0D toP0D(String name, int axis){
			if (axis == 0)
				return xP0D(name);
			else if (axis == 1)
				return new P0D(name, false, intensity_au);
			else if (axis == 2)
				return new P0D(name, false, err_au);
			else
				return null;
		}
		
		private P0D xP0D(String name){
			if (nano)
				return new P0D(name, false, wavelength_nm);
			else
				return new P0D(name, false, energy_eV);
		}
					
		public double[] axisValues(int axis){
			if (axis == 0)
				return xValues();
			else if (axis == 1)
				return intensity_au.getArray();
			else if (axis == 2)
				return err_au.getArray();
			else
				return null;
		}
		
		private double[] xValues(){
			if (nano)
				return wavelength_nm.getArray();
			else
				return energy_eV.getArray();
		}
		
		public ITuple toTuple(String name){
			return toTuple(AidaFactories.TREE, name);
		}
		
		public ITuple toTuple(ITree tree, String pathAndName){
			ITupleFactory tupleFac = AidaFactories.ANALYSIS_FACTORY.createTupleFactory(tree);
			boolean sep = pathAndName.contains(File.separator);
			String[] sepPathName = new String[2];
			if (sep)
				sepPathName = splitPathName(pathAndName);
			else
				sepPathName = noSplitPathName(pathAndName);
			String columns = "double " + WL_TITLE + ", double" + EN_TITLE + ", double" + INTENS_TITLE + ", double" + ERR_TITLE;
			ITuple tuple = tupleFac.create(sepPathName[0], sepPathName[1], columns);
			fillTuple(tuple);
			return tuple;
		}
		
		private String[] splitPathName(String pathAndName){
			String[] sepPathName = new String[2];
			String[] allPart = pathAndName.split(File.separator);
			StringBuilder sb = new StringBuilder();
			int partLength = allPart.length;
			for (int i=0; i<partLength - 1; i++){
				sb.append(allPart[i]);
				if (i<partLength - 2)
					sb.append(File.separator);
			}
			sepPathName[0] = sb.toString();
			sepPathName[1] = allPart[partLength - 1];
			return sepPathName;
		}
		
		private String[] noSplitPathName(String pathAndName){
			return new String[]{"", pathAndName};
		}
		
		private void fillTuple(ITuple tuple){
			tuple.reset();
			tuple.start();
			for (int i=0; i<intensity_au.size(); i++){
				tuple.fill(0, wavelength_nm.get(i));
				tuple.fill(1, energy_eV.get(i));
				tuple.fill(2, intensity_au.get(i));
				tuple.fill(3, err_au.get(i));
				tuple.addRow();
			}
		}
		
		public IDataPointSet toDPS(String name){
			return toDPS(AidaFactories.TREE, name);
		}
		
		public IDataPointSet toDPS(ITree tree, String pathAndName){
			IDataPointSetFactory dpsFac = AidaFactories.ANALYSIS_FACTORY.createDataPointSetFactory(tree);
			IDataPointSet dps = dpsFac.create(pathAndName, 2);
			if (nano)
				fillDPS(dps, wavelength_nm);
			else
				fillDPS(dps, energy_eV);
			return dps;
		}
		
		private void fillDPS(IDataPointSet dps, P0D xP0D){
			for (int i=0; i<xP0D.size(); i++){
				IDataPoint dp = dps.addPoint();
				IMeasurement mX = dp.coordinate(0);
				double x = xP0D.get(i);
				mX.setValue(x);
				IMeasurement mY = dp.coordinate(1);
				double y = intensity_au.get(i);
				double yErr = err_au.get(i);
				mY.setValue(y);
				mY.setErrorPlus(yErr);
				mY.setErrorMinus(yErr);
			}
		}
		
		public IHistogram1D toHisto(String name){
			return toHisto(AidaFactories.TREE, name);
		}
		
		public IHistogram1D toHisto(ITree tree, String pathAndName){
			if (nano)
				return fillHisto(tree, pathAndName, wavelength_nm);
			else
				return fillHisto(tree, pathAndName, energy_eV);
		}
		
		private IHistogram1D fillHisto(ITree tree, String pathAndName, P0D xP0D){
			IHistogramFactory histoFac = AidaFactories.ANALYSIS_FACTORY.createHistogramFactory(tree);
			int bin = intensity_au.size();
			double min = xP0D.getMin();
			double max = xP0D.getMax();
			IHistogram1D histo = histoFac.createHistogram1D(pathAndName, bin, min, max);
			for (int i=0; i<bin; i++){
				double x = xP0D.get(i);
				double y = intensity_au.get(i);
				histo.fill(x, y);
			}
			return histo;
		}
		
		public void showPlot(){
			doPlot();
			showPlot = true;
			plotter.visible(showPlot);
		}
		
		public void savePlot(String pathAndName){
			doPlot();
			for (int i = 0; i < FileExtensions.FIG_TYPES.length; i++){
				plotter.export(pathAndName + FileExtensions.FIG_TYPES[i]);
				System.out.println("Writing File : " + pathAndName + FileExtensions.FIG_TYPES[i]);
			}
		}
		
		private void doPlot(){
			plotSpectro.clear();
			plotSpectro = toP1D("Spectrum");
			plotSpectro.setStyle("lp");
			plotSpectro.setSymbolSize(0);
			plotSpectro.setPenWidth(2);
			plotSpectro.setColor(Color.BLUE);
			plotSpectro.setErrAll(false);
			double xMin = plotSpectro.getMin(0);
			double xMax = plotSpectro.getMax(0);
			double yMin = plotSpectro.getMin(1);
			double yMax = plotSpectro.getMax(1);
			plotter.draw(plotSpectro);
			plotter.setRange(xMin, xMax, yMin, yMax);
			plotter.update();
			double[] val = plotSpectro.getArrayY();
			double[] valErr = plotSpectro.getArrayYupper();
			//for (int i = 0; i < val.length; i++){
			//	System.out.println("val = " + val[i]);
			//	System.out.println("valErr = " + valErr[i]);
			//}
		}
		
		public void roundAbscissa(){
			wlP0D.clear(); 
			enP0D.clear();
			for (int i=0; i<wavelength_nm.size(); i++){
				double val = wavelength_nm.get(i);
				double round = Math.rint(val);
				wlP0D.add(round); 
				enP0D.add(ENERGY_WAVELENGTH / round);
				//wavelength_nm.set(i, round);
				//double enVal = energy_eV.get(i);
				//energy_eV.set(i, ENERGY_WAVELENGTH / round);
			}
			updateWL();
			updateEn();
		}
			
		public void removeOutliers(int halfNumPnts, double numSigmas){
			//int halfNumPnts = 2;
			//double numSigmas = 3;
			class LPCMLOutliersImp implements Outliers{
				
				public boolean isThereOutliers(int halfNumPnts, double numSigmas){
					boolean outl = false;	
					int size = intensity_au.size();
					int i = 0;
					while (!outl && i < size){
						outl = isOutlier(i, halfNumPnts, numSigmas);
						i++;
					}
					return outl;
				}
				
				public void removeOutliers(int halfNumPnts, double numSigmas){
					boolean[] outliers = findOutliers(halfNumPnts, numSigmas);
					clearTempP0Ds();
					int size = outliers.length;
					for(int i = 0; i < size; i++){
						if (!outliers[i])
							fillTempP0Ds(i);
					}
					updateAll();
				}
				
				public boolean[] findOutliers(int halfNumPnts, double numSigmas){
					int size = intensity_au.size();
					boolean[] outliers = new boolean[size];
					for (int i = 0; i < size; i++){
						outliers[i] = isOutlier(i, halfNumPnts, numSigmas);
						//if (outliers[i])
						//	System.out.println(wavelength_nm.get(i));
					}
					return outliers;
				}
				
				private boolean isOutlier(int index, int halfNumPnts, double numSigmas) {
					double value = intensity_au.get(index);
					double[] array = surroundArray(index, halfNumPnts);
					double average  = calcAverage(array);
					double variance = calcVariance(array, average);
					double distSquared = Math.pow(average - value, 2);
					double refVariance = variance * Math.pow(numSigmas, 2);
					return (distSquared > refVariance);
				}
				
				private double calcAverage(double[] array){
					int size = array.length;
					double average = 0.0;
					for (int i = 0; i < size; i++){
						average += array[i];
					}
					return average / (double) size;
				}
				
				private double calcVariance(double[] array, double average){
					int size = array.length;
					double variance = 0.0;
					for (int i = 0; i < size; i++){
						variance += Math.pow(array[i] - average, 2);
					}
					return variance / (double) size;
				}
				
				private double[] surroundArray(int index, int halfNumPnts){
					if (index < halfNumPnts)
						return calcLowArray(index, halfNumPnts);
					else if (index >= intensity_au.size() - halfNumPnts)
						return calcHighArray(index, halfNumPnts);
					else
						return calcCoreArray(index, halfNumPnts);
				}
				
				private double[] calcLowArray(int index, int halfNumPnts){
					return calcArray(0, index + halfNumPnts, index);
				}
				
				private double[] calcHighArray(int index, int halfNumPnts){
					return calcArray(index - halfNumPnts, intensity_au.size() - 1, index);
				}
				
				private double[] calcCoreArray(int index, int halfNumPnts){
					return calcArray(index - halfNumPnts, index + halfNumPnts, index);
				}
				
				private double[] calcArray(int start, int stop, int index){
					//double[] array = new double[stop - start];
					P0D array = new P0D();
					for (int i = start; i <= stop; i++){
						if (i != index)
							array.add(intensity_au.get(i));
					}
					return array.getArray();
				}
			}
			
			LPCMLOutliersImp outliers = new LPCMLOutliersImp();
			int cnt = 0;
			while (outliers.isThereOutliers(halfNumPnts, numSigmas)){
				outliers.removeOutliers(halfNumPnts, numSigmas);
				cnt++;
			}
			System.out.println("No more outliers found after " + cnt + " iteration(s)");
		}
		
		public void averageMultipleValues(){
			clearTempP0Ds();
			//for (int i=0; i<wavelength_nm.size(); i++){
			int i = 0;
			int size = wavelength_nm.size();
			while (i < size){
				double xVal = wavelength_nm.get(i);
				double yVal = intensity_au.get(i);
				double yErr = err_au.get(i);
				//System.out.println("wl_i = " + xVal);
				int j = i;
				int cnt = 1;
				while(wavelength_nm.get(j) == xVal && j < size - 1){
					//System.out.println("wl_j = " + xVal);
					yVal += intensity_au.get(j);
					yErr += err_au.get(j);
					j++;
					cnt++;
				}
				yVal /= (double) cnt;
				yErr /= (double) cnt;
				wlP0D.add(xVal);
				enP0D.add(ENERGY_WAVELENGTH / xVal);
				intensP0D.add(yVal);
				errP0D.add(yErr);
				i = j;
				if (i == size -1)
					break;
			}
			updateAll();
		}
		
		public void smooth(int average){
			int size = intensity_au.size();
			//intensP0D.clear();
			initTempIntensP0D();
			initTempErrP0D();
			for (int i = 0; i < size; i++){
				//double[] yVal = {0.0, 0.0};
				int[] edges = new int[2];
				if ((i - average) < 0)
					edges =  new int[]{0,  i + average};
				else if ((i - average) >= 0 && (i + average) < size)
					edges = new int[]{i - average,  i + average};
				else
					edges =  new int[]{i - average,  size - 1};
				//System.out.println("======================>");
				double numVal = (average * 2.0) + 1.0;
				double[] yVal = summedIntensities(edges);
				intensP0D.add(yVal[0] / numVal);
				errP0D.add(yVal[1] / numVal);
				//System.out.println(yVal[0] / numVal);
				//System.out.println(yVal[1] / numVal);
			}
			updateIntensity();
			updateErr();
		}
		
		private double[] summedIntensities(int[] edge){
			double[] val = {0.0, 0.0};
			for (int i = edge[0]; i <= edge[1]; i++){
				val[0] += intensity_au.get(i);
				val[1] += err_au.get(i);
			}
			return val;
		}
		
		public void cutEdges(double min, double max){
			if (nano)
				cutEdgesWL(min, max);
			else
				cutEdgesEn(min, max);
		}
		
		private void cutEdgesWL(double min, double max){
			int indexMin = 0;
			int indexMax = 0;
			if (wavelength_nm.contains(min))
				indexMin = wavelength_nm.find(min);
			else
				indexMin = findMinIndexWL(min);
			if (wavelength_nm.contains(max))
				indexMax = wavelength_nm.find(max);
			else
				indexMax = findMaxIndexWL(max);
			doEdgesCut(indexMin, indexMax);
		}
		
		private void cutEdgesEn(double min, double max){
			int indexMin = 0;
			int indexMax = 0;
			if (energy_eV.contains(min))
				indexMin = energy_eV.find(min);
			else
				indexMin = findMinIndexWL(min);
			if (energy_eV.contains(max))
				indexMax = energy_eV.find(max);
			else
				indexMax = findMaxIndexWL(max);
			doEdgesCut(indexMin, indexMax);
		}
		
		private void doEdgesCut(int indexMin, int indexMax){
			clearTempP0Ds();
			for (int i = indexMin; i < indexMax + 1; i++){
				double val = 0.0;
				val = wavelength_nm.get(i);
				wlP0D.add(val);
				val = energy_eV.get(i);
				enP0D.add(val);
				val = intensity_au.get(i);
				intensP0D.add(val);
				val = err_au.get(i);
				errP0D.add(val);
			}
			updateAll();
		}
		
		public double integrate(){
			IMeasurement meas = integrateAndErr();
			return meas.value();
		}
		
		public double integrate(double min, double max){
			IMeasurement meas = integrateAndErr(min, max);
			return meas.value();
		}
		
		public IMeasurement integrateAndErr(){
			int indexMin = 0;
			int indexMax = intensity_au.size();
			if (nano)
				return calcIntegWL(indexMin, indexMax);
			else
				return calcIntegEn(indexMin, indexMax);
		}
		
		public IMeasurement integrateAndErr(double min, double max){
			if (nano)
				return integrateWL(min, max);
			else
				return integrateEn(min, max);
		}
		
		private IMeasurement integrateWL(double min, double max){
			int indexMin = 0;
			int indexMax = 0;
			if (wavelength_nm.contains(min))
				indexMin = wavelength_nm.find(min);
			else
				indexMin = findMinIndexWL(min);
			if (wavelength_nm.contains(max))
				indexMax = wavelength_nm.find(max);
			else
				indexMax = findMaxIndexWL(max);
			return calcIntegWL(indexMin, indexMax);
		}
		
		private IMeasurement calcIntegWL(int indexMin, int indexMax){
			double integral = 0.0;
			double err = 0.0;
			for (int i = indexMin; i < indexMax - 1; i++){
				double dWL = wavelength_nm.get(i + 1) - wavelength_nm.get(i);
				integral += (dWL * intensity_au.get(i));
				err += (dWL * err_au.get(i));
			}
			return new Measurement(integral, err);
		}
			
		private IMeasurement integrateEn(double min, double max){
			int indexMin = 0;
			int indexMax = 0;
			if (energy_eV.contains(min))
				indexMin = energy_eV.find(min);
			else
				indexMin = findMinIndexEn(min);
			if (energy_eV.contains(max))
				indexMax = energy_eV.find(max);
			else
				indexMax = findMinIndexEn(max);
			return calcIntegEn(indexMin, indexMax);
		}
		
		private IMeasurement calcIntegEn(int indexMin, int indexMax){
			double integral = 0.0;
			double err = 0.0;
			for (int i = indexMin; i < indexMax - 1; i++){
				double dWL = energy_eV.get(i + 1) - energy_eV.get(i);
				integral += (dWL * intensity_au.get(i));
				err += (dWL * err_au.get(i));
			}
			return new Measurement(integral, err);
		}
		
		private void updateWL(){
			//wavelength_nm.clear();
			initWavelengthP0D();
			for (int i=0; i<wlP0D.size(); i++){
				double val = wlP0D.get(i);
				wavelength_nm.add(val);
			}
			//wlP0D.clear();
			initTempWavelengthP0D();
		}
			
		private void updateEn(){
			//energy_eV.clear();
			initEnP0D();
			for (int i = 0; i < enP0D.size(); i++){
				double val = enP0D.get(i);
				energy_eV.add(val);
			}
			//enP0D.clear();
			initTempEnP0D();
		}
			
		private void updateIntensity(){
			//intensity_au.clear();
			initIntensP0D();
			for (int i=0; i<intensP0D.size(); i++){
				double val = intensP0D.get(i);
				intensity_au.add(val);
			}
			//intensP0D.clear();
			initTempIntensP0D();
		}
		
		private void updateErr(){
			//err_au.clear();
			initErrP0D();
			for (int i=0; i<errP0D.size(); i++){
				double val = errP0D.get(i);
				err_au.add(val);
			}
			//errP0D.clear();
			initTempErrP0D();
		}
		
		private void updateAll(){
			//wavelength_nm.clear();
			//energy_eV.clear();
			//intensity_au.clear();
			initAllP0Ds();
			for (int i=0; i<wlP0D.size(); i++){
				double val = 0.0;
				val = wlP0D.get(i);
				wavelength_nm.add(val);
				val = enP0D.get(i);
				energy_eV.add(val);
				val = intensP0D.get(i);
				intensity_au.add(val);
				val = errP0D.get(i);
				err_au.add(val);
			}
			clearTempP0Ds();
		}
		
		private int findMinIndexWL(double min){
			int index = 0;
			while (wavelength_nm.get(index) < min)
				index++;
			return index + 1;
		}
		
		private int findMaxIndexWL(double max){
			int index = wavelength_nm.size() - 1;
			while (wavelength_nm.get(index) > max)
				index--;
			return index - 1;
		}
		private int findMinIndexEn(double min){
			int index = energy_eV.size() - 1;
			while (energy_eV.get(index) > min)
				index--;
			return index - 1;
		}
		
		private int findMaxIndexEn(double max){
			int index = 0;
			while (energy_eV.get(index) < max)
				index++;
			return index + 1;
		}
							
		private void fillTempP0Ds(int index){
			wlP0D.add(wavelength_nm.get(index));
			enP0D.add(energy_eV.get(index));
			intensP0D.add(intensity_au.get(index));
			errP0D.add(err_au.get(index));
		}
		
		private void clearTempP0Ds(){
			//wlP0D.clear();
			//enP0D.clear();
			//intensP0D.clear();
			initAllTempP0Ds();
		}
	}
}
